package com.loan.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.loan.common.constant.Constants;
import com.loan.common.core.page.PageData;
import com.loan.common.service.BaseService;
import com.loan.common.utils.BeanCopyUtils;
import com.loan.common.utils.EntityUtils;
import com.loan.common.utils.StringUtils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 基础服务类，所有的service都要继承
 *
 * @author shiguang
 */
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements BaseService<T> {

    @Autowired
    protected M baseMapper;

    /**
     * 判断数据库操作是否成功 注意：该方法为integer判断，不可传入int基本类型
     *
     * @param result 数据库操作返回影响条数
     * @return
     */
    protected static boolean retBool(Integer result) {
        return SqlHelper.retBool(result);
    }

    /**
     * 获取分页对象
     *
     * @param params            分页查询参数
     * @param defaultOrderField 默认排序字段
     * @param isAsc             排序方式
     * @return
     */
    protected IPage<T> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
        //分页参数
        long curPage = 1;
        long limit = 10;
        if (params.get(Constants.PAGE) != null) {
            curPage = Long.parseLong((String) params.get(Constants.PAGE));
        }
        if (params.get(Constants.LIMIT) != null) {
            limit = Long.parseLong((String) params.get(Constants.LIMIT));
        }
        //分页对象
        Page<T> page = new Page<>(curPage, limit);
        //排序字段
        String orderField = (String) params.get(Constants.ORDER_FIELD);
        String order = (String) params.get(Constants.ORDER);
        //前端字段排序
        if (StringUtils.isNotBlank(orderField) && StringUtils.isNotBlank(order)) {
            if (Constants.ASC.equalsIgnoreCase(order)) {
                return page.addOrder(OrderItem.asc(orderField));
            } else {
                return page.addOrder(OrderItem.desc(orderField));
            }
        }
        //默认排序
        if (isAsc) {
            page.addOrder(OrderItem.asc(defaultOrderField));
        } else {
            page.addOrder(OrderItem.desc(defaultOrderField));
        }
        return page;
    }

    protected <T> PageData<T> getPageData(List<?> list, long total, Class<T> target) {
        List<T> targetList = BeanCopyUtils.copyList(list, target);
        return new PageData<>(targetList, total);
    }

    protected <T> PageData<T> getPageData(IPage page, Class<T> target) {
        return getPageData(page.getRecords(), page.getTotal(), target);
    }

    protected Map<String, Object> paramsToLike(Map<String, Object> params, String... likes) {
        for (String like : likes) {
            String val = (String) params.get(like);
            if (StringUtils.isNotBlank(val)) {
                params.put(like, "%" + val + "%");
            } else {
                params.put(like, null);
            }
        }
        return params;
    }

    /**
     * 逻辑删除 最终还是调用updateBatchById方法，不建议使用
     *
     * @param ids    ids
     * @param entity 实体
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean logicDelete(Long[] ids, Class<T> entity) {
        List<T> entityList = EntityUtils.deleteBy(ids, entity);
        return updateBatchById(entityList);
    }

    /**
     * 逻辑删除 推荐使用
     *
     * @param ids
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void logicDelete(Long[] ids) {
        for (Long id : ids) {
            this.deleteById(id);
        }
    }

    protected Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(getClass(), getClass(),1);
    }

    /**
     * 批量删除 sqlSession
     *
     * @return
     */
    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(currentModelClass());
    }

    /**
     * 批量操作sqlSession
     *
     * @param sqlSession
     */
    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(currentModelClass()));
    }

    /**
     * 获取SqlStatement
     *
     * @param sqlMethod
     * @return
     */
    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
    }

    @Override
    public boolean insert(T entity) {
        return BaseServiceImpl.retBool(baseMapper.insert(entity));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(Collection<T> entityList) {
        return insertBatch(entityList, 100);
    }

    /**
     * 批量插入
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(Collection<T> entityList, int batchSize) {
        SqlSession batchSqlSession = sqlSessionBatch();
        int i = 0;
        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
        try {
            for (T anEntityList : entityList) {
                batchSqlSession.insert(sqlStatement, anEntityList);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }finally {
            closeSqlSession(batchSqlSession);
        }
        return true;
    }

    @Override
    public boolean updateById(T entity){
        return BaseServiceImpl.retBool(baseMapper.updateById(entity));
    }

    @Override
    public boolean update(T entity, Wrapper<T> updateWrapper){
        return BaseServiceImpl.retBool(baseMapper.update(entity,updateWrapper));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public  boolean updateBatchById(Collection<T> entityList){
        return updateBatchById(entityList,30);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateBatchById(Collection<T> entityList,int batchSize){
        if (CollectionUtils.isEmpty(entityList)){
            throw new IllegalArgumentException("Error: entityList must not be empty");
        }
        SqlSession batchSqlSession = sqlSessionBatch();
        int i = 0;
        String sqlStatement =sqlStatement(SqlMethod.UPDATE_BY_ID);
        try {
            for (T anEntityList  : entityList){
                MapperMethod.ParamMap<Object> param = new MapperMethod.ParamMap<>();
                param.put(com.baomidou.mybatisplus.core.toolkit.Constants.ENTITY,anEntityList);
                batchSqlSession.update(sqlStatement,param);
                if (i >=1 && i %batchSize == 0){
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }finally {
            closeSqlSession(batchSqlSession);
        }
        return true;
    }

    @Override
    public T selectById(Serializable id){
        return baseMapper.selectById(id);
    }

    @Override
    public boolean deleteById(Serializable id){
        return SqlHelper.retBool(baseMapper.deleteById(id));
    }

    @Override
    public boolean deleteBatchIds(Collection<? extends  Serializable> idList){
        return SqlHelper.retBool(baseMapper.deleteBatchIds(idList));
    }

}
